Adding Boundary Pores


In [1]:
import numpy as np
import openpnm as op
np.random.seed(10)
%matplotlib inline

Start by creating a Delaunay network. Because it uses random base points it will better illustrate the process of adding boundary pores to arbitrary networks:


In [2]:
pn = op.network.Delaunay(num_points=200, shape=[1, 1, 0])
print(pn)


――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
openpnm.network.Delaunay : net_01
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
#     Properties                                    Valid Values
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
1     pore.coords                                     248 / 248  
2     throat.conns                                    601 / 601  
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
#     Labels                                        Assigned Locations
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
1     pore.all                                      248       
2     pore.back                                     9         
3     pore.bottom                                   248       
4     pore.boundary                                 48        
5     pore.front                                    12        
6     pore.internal                                 200       
7     pore.left                                     14        
8     pore.right                                    13        
9     pore.surface                                  44        
10    pore.top                                      248       
11    throat.all                                    601       
12    throat.boundary                               0         
13    throat.internal                               553       
14    throat.surface                                48        
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――

As can be seen in the above printout, the Delaunay class predefines many labels including boundaries and sides. In fact, as can be seen in the plot below, the Delaunay class also adds boundary pores to the topology. (Note that the Delaunay network is generated randomly so your's will not look the same, nor have the same number of total pores and throats). In this case, the location of the boundary pores is determined from the Voronoi cell that surrounds each Delaunay point, so the boundary cells apper to be randomly oriented relative to the internal pore they are connected with. In the example that follows, we'll be removing these pores, then adding boundary pores in a manual way.


In [3]:
#NBVAL_IGNORE_OUTPUT
fig = op.topotools.plot_connections(network=pn)
fig = op.topotools.plot_coordinates(network=pn, fig=fig, c='r')
fig.set_size_inches((7, 7))


For the purpose of this tutorial, we will trim these boundary pores from the network since we'll be adding our own.


In [4]:
op.topotools.trim(network=pn, pores=pn.pores('boundary'))
print(pn)


――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
openpnm.network.Delaunay : net_01
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
#     Properties                                    Valid Values
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
1     pore.coords                                     200 / 200  
2     throat.conns                                    553 / 553  
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
#     Labels                                        Assigned Locations
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
1     pore.all                                      200       
2     pore.back                                     0         
3     pore.bottom                                   200       
4     pore.boundary                                 0         
5     pore.front                                    0         
6     pore.internal                                 200       
7     pore.left                                     0         
8     pore.right                                    0         
9     pore.surface                                  44        
10    pore.top                                      200       
11    throat.all                                    553       
12    throat.boundary                               0         
13    throat.internal                               553       
14    throat.surface                                0         
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――

Plotting the network now shows the missing pores. Our goal will be re-add boundary pores to each face.


In [5]:
#NBVAL_IGNORE_OUTPUT
fig = op.topotools.plot_connections(network=pn)
fig = op.topotools.plot_coordinates(network=pn, fig=fig, c='r')
fig.set_size_inches((7, 7))


Find surface pores

The topotools module in OpenPNM provides many handy helper functions for dealing with topology. We'll first use the find_surface_pores function. It works be specifying the location of a set of marker points outside the domain, then performing a Delaunay tessellation between these markers and the network pores. Any pores that form a simplex with the marker points are considered to be on the surface. By default OpenPNM will place one marker on each edge of the domain in an attempt to find all the surfaces. In our case, we will specify them manually to only find one face.

Specifying the markers can be a challenge. If we only specify a single marker, we will only find a limited number of surface pores due to the way the triangulation works.


In [6]:
#NBVAL_IGNORE_OUTPUT
markers = np.array([[-0.1, 0.5]])
op.topotools.find_surface_pores(network=pn, markers=markers, label='left_surface')
fig = op.topotools.plot_connections(network=pn)
fig = op.topotools.plot_coordinates(network=pn, pores=pn.pores('left_surface'), fig=fig, c='r')
fig.set_size_inches((7, 7))


As can be seen, some of the pores in deeper recesses of the surface were not found by this method. If we want to be certain of finding all the surface pores on the left side of the domain we can add more markers:


In [7]:
#NBVAL_IGNORE_OUTPUT
markers = np.array([[-0.1, 0.2], [-0.1, 0.4], [-0.1, 0.6], [-0.1, 0.8]])
op.topotools.find_surface_pores(network=pn, markers=markers, label='left_surface')
fig = op.topotools.plot_connections(network=pn)
fig = op.topotools.plot_coordinates(network=pn, pores=pn.pores('left_surface'), fig=fig, c='r')
fig.set_size_inches((7, 7))


Now we've captured several more pores. In some cases we may actually get more than we wanted, including some that are more correctly on the bottom of the domain. This is why finding surfaces requires a careful touch, although this problem becomes less important in domains with more pores.

Cloning surface pores

Next we want to take the newly labeled surface pores and 'clone' them. This creates new pores in the network that are physically located in the same place as their 'parents'. They are also connected only to their 'parents' by default which is what we want, though this can be changed using the mode argument. In the following code, we tell the function to clone the 'left_surface' pores and to give them a new label of 'left_boundary'.


In [8]:
op.topotools.clone_pores(network=pn, pores=pn.pores('left_surface'), labels=['left_boundary'])

Now that we've cloned the pores, we need to move them. In this case we want them to all site on teh x=0 boundary face. We can do this by directly altering the 'pore.coords' array:


In [9]:
Ps = pn.pores('left_boundary')
coords = pn['pore.coords'][Ps]
coords *= [0, 1, 1]
pn['pore.coords'][Ps] = coords
print(pn)


――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
openpnm.network.Delaunay : net_01
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
#     Properties                                    Valid Values
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
1     pore.coords                                     212 / 212  
2     throat.conns                                    565 / 565  
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
#     Labels                                        Assigned Locations
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
1     pore.all                                      212       
2     pore.back                                     0         
3     pore.bottom                                   200       
4     pore.boundary                                 0         
5     pore.front                                    0         
6     pore.internal                                 200       
7     pore.left                                     0         
8     pore.left_boundary                            12        
9     pore.left_surface                             12        
10    pore.right                                    0         
11    pore.surface                                  44        
12    pore.top                                      200       
13    throat.all                                    565       
14    throat.boundary                               0         
15    throat.internal                               553       
16    throat.left_boundary                          12        
17    throat.surface                                0         
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――

The above code will set the x-coordinate of each of the cloned pores to 0, while maintaining the other coordinates the same. The result is:


In [10]:
#NBVAL_IGNORE_OUTPUT
fig = op.topotools.plot_connections(network=pn)
fig = op.topotools.plot_coordinates(network=pn, pores=pn.pores('left_surface'), fig=fig, c='r')
fig = op.topotools.plot_coordinates(network=pn, pores=pn.pores('left_boundary'), fig=fig, c='g')
fig.set_size_inches((7, 7))